#include "ImageShack.h"
#include <QDebug>


ImageShackClass::ImageShackClass()
	: QObject()
{
	About.Name          = "ImagesShack";
	About.Description   = "ImageShack uploader plugin";
	About.Hosting       = "ImageShack";
	About.HostingUrl    = "http://imageshack.us/";
	About.Author        = "Name_No (Nick Malakhov)";
	About.email         = "zasada_shaman@list.ru";
	About.version       = "0.1";
	About.CmdlParameter = "imageshack";
	About.icon.addPixmap(QPixmap(QString::fromUtf8(":/allstuff/imageshack.png")), QIcon::Normal, QIcon::Off);
//
	Hosting.Host    = "www.imageshack.us";
	Hosting.URI     = "/index.php";
/*	// my testing server
	Hosting.Host    = "mediacenter";
	Hosting.URI     = "/post.php";
*/
	Hosting.URL     = "http://www.imageshack.us/index.php";
	Hosting.User_Agent = "Name_No image uploader";
//	from 'resize' list
	resizes << "100x100" << "150x150" << "320x320" << "640x640"
		<< "800x800" << "1024x1024" << "1280x1280" << "1600x1600" << "resample";
//
	Config = new QSettings(); // appname. organization and orher stuff set in main application
	Config->beginGroup("ImageShackUploader");
	connect(this, SIGNAL(totalError(QString)), SLOT(globalDestruction(QString)));

}



QLayout* ImageShackClass::getSettingsTab(QWidget *Tab)
{
	MySettingsTab.setupUi(Tab);
	MySettingsTab.optimageBox->setChecked(Config->value("optimage", false).toBool());
	MySettingsTab.resizeComboBox->setCurrentIndex(Config->value("optsize", 0).toInt());
	MySettingsTab.removeFugeCheckBox->setChecked(Config->value("rembar", false).toBool());
	connect(MySettingsTab.optimageBox, SIGNAL(toggled(bool)), this, SLOT(resizeTogled(bool)));
	connect(MySettingsTab.resizeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(resizePolicySelected(int)));
	connect(MySettingsTab.removeFugeCheckBox, SIGNAL(toggled(bool)), this, SLOT(rembarTogled(bool)));
	return(MySettingsTab.gridLayout);
}

void ImageShackClass::startUpload(QList<ImageToUpload> images, signalSlotStratum *stratum)
{
	qDebug() << "Start uploading";
	Stratum = stratum;
	imageIterator = new QListIterator<ImageToUpload>(images);
	uploadResult.clear();

	foreach(ImageToUpload image, images)
	{
		totalSize += image.size;
	}
	uploadedSize = 0;
	currentFileIndex = 0;
	http_h = 0; // to avoid race conditions.
// some connects and disconnects here
	connect(this, SIGNAL(totalError(QString)), Stratum, SIGNAL(totalError(QString)));
	connect(this, SIGNAL(fileError(int,QString)), Stratum, SIGNAL(fileUploadFailed(int,QString)));
	connect(this, SIGNAL(uploadComplete(QList<UploadResult>)),
			Stratum, SIGNAL(uploadComplete(QList<UploadResult>)));
	connect(this, SIGNAL(fileUploaded()), SLOT(startNextFile()));
	connect(this, SIGNAL(fileUploaded(int, UploadResult)), Stratum, SIGNAL(fileUploaded(int, UploadResult)));
	connect(this, SIGNAL(startNextFileSig(int)), Stratum, SIGNAL(startNextFile(int)));
	connect(Stratum, SIGNAL(cancelUpload()), this, SLOT(Canceled()));

	httpRequestAborted = false;
//	fileUploadFailed = false;
	emit fileUploaded(); // actually, call startNextFile();
 
}

void ImageShackClass::startNextFile()
{
	httpFileAborted = false;
	if ( imageIterator->hasNext() and !httpRequestAborted )
	{
		ImageToUpload image = imageIterator->next();
		currentFileIndex = image.index;
		currentImageUploaded = image;
		emit startNextFileSig(currentFileIndex);

		serverAnswer = new QBuffer();
		if (!openDevice(serverAnswer, QIODevice::WriteOnly)) return;
		http_h = new QHttp(Hosting.Host);
		connect(http_h, SIGNAL(requestFinished(int, bool)), this, SLOT(httpRequestFinished(int, bool)));
		connect(http_h, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
				this, SLOT(readResponseHeader(const QHttpResponseHeader &)));
		connect(http_h, SIGNAL(dataSendProgress(int,int)), Stratum, SIGNAL(updateStatus(int,int)));
		httpRequestAborted = false;
//		QString boundary = randomString( 42 + 13 ); // I dont know why 42+13, I took it from konqueror :-)
		PostData Data = collectData(image);//, boundary); // QByteArray, just subclassed
//		if (Data == 0) return;
		qDebug() << "Data.size:" << Data.size();

//		QHttpRequestHeader *header = headers(boundary, Data.size());
		if ( http_h != 0 ) {
			httpPostId = http_h->request(headers(Data.getBoundary(), Data.size()), Data, serverAnswer); // post gone. other movements in slots
		}
//		delete(header);
//		header = NULL;
	} else {
		while ( imageIterator->hasNext() and httpRequestAborted ) // canceling other images
		{
			ImageToUpload image = imageIterator->next();
			qDebug() << "skipping images:" << image.ImageName;
		}
		emit uploadComplete(uploadResult);
		disconnect(this, SIGNAL(totalError(QString)), Stratum, SIGNAL(totalError(QString)));
		disconnect(this, SIGNAL(uploadComplete(QList<UploadResult>)),
			Stratum, SIGNAL(uploadComplete(QList<UploadResult>)));
		disconnect(this, SIGNAL(fileUploaded()), this, SLOT(startNextFile()));
		disconnect(Stratum, SIGNAL(cancelUpload()), this, SLOT(Canceled()));
		delete Stratum;
		Stratum = 0;
		return;
	}
}

bool ImageShackClass::openDevice(QIODevice *buffer, QIODevice::OpenMode Mode)
{
	if (! buffer->isOpen() ) {
		if (!buffer->open(Mode)) { 
			qWarning() << "HTTP post: can't open given file"
			<< "(" + buffer->errorString() + ")";
			emit totalError(buffer->errorString());
			return false;
		}
	} else {
		if ( !buffer->reset() ) {
			qWarning() << "file is open, but I can't seek to beginning:" << buffer->errorString();
			emit totalError(buffer->errorString());
			return false;
		}
	}
	return true;
}

PostData ImageShackClass::collectData(ImageToUpload imageStruct)//, QString boundary)
{
	if ( !openDevice(imageStruct.Image, QIODevice::ReadOnly) ) return 0;
	PostData data;
//	data.setBoundary(boundary);
// imageshack xml=yes field
	data.appendTextField("xml", "yes");
// image itself
	data.appendBinaryField("fileupload", imageStruct.ImageName, imageStruct.Image->readAll());
// form params
	if ( Config->value("rembar", false).toBool() )
	{
		data.appendTextField("rembar", 1);
	}
	if ( Config->value("optimage", false).toBool() ) {
		data.appendTextField("optimage", 1);
		data.appendTextField("optsize", resizes.at(Config->value("optsize").toInt()));
	}
// the end
	data.finalize();
	return data;
}



QHttpRequestHeader ImageShackClass::headers(QString boundary, qint64 length)
{
	QHttpRequestHeader header("POST", Hosting.URI);
	header.setValue("Host", Hosting.Host);
	header.setValue( "User-Agent", Hosting.User_Agent);
	header.setValue("Cache-Control", "no-cache");
	header.setValue("Accept","*/*");
	header.setValue("Content-type", "multipart/form-data,  boundary="+boundary);
	header.setContentLength(length);
	return header;
}

void ImageShackClass::readResponseHeader(const QHttpResponseHeader &responseHeader)
{
	if (responseHeader.statusCode() != 200)
	{
		qWarning() << "HTTP Download failed:" << responseHeader.statusCode() << responseHeader.reasonPhrase();
		httpFileAborted = true;
		emit fileError(currentFileIndex, responseHeader.reasonPhrase());
		http_h->abort();
		return;
	} else {
		qDebug() << "got header:" << responseHeader.statusCode() << responseHeader.reasonPhrase();
	}
}
void ImageShackClass::httpRequestFinished(int requestId, bool error)
{
	if (requestId != httpPostId) // it's not My request.
		return;
	serverAnswer->close();
	http_h->deleteLater();
	http_h = 0;
	UploadResult *currenFileResultStruct = new UploadResult; // struct about current file.
	myCurrentResultHash = new QHash<QString, QString>(); // hash with xml data. Filled from xml parser.
	if ( error or httpFileAborted == true ) {
		qWarning() << ":-( error. sorry";
		currenFileResultStruct->success = false;
		httpFileAborted = true;
//		return;
	} else {
		currenFileResultStruct->success = parse_xml();
		fillStruct(currenFileResultStruct, myCurrentResultHash);
	}
	uploadResult.append(*currenFileResultStruct);
	emit fileUploaded(currentFileIndex, *currenFileResultStruct);
	uploadFinish();
	delete(currenFileResultStruct);
}

bool ImageShackClass::parse_xml()
{
	if (!openDevice(serverAnswer, QIODevice::ReadOnly)) return false;

	QXmlInputSource *source = new QXmlInputSource(serverAnswer);
	SaxHandler handler(myCurrentResultHash);
	QXmlSimpleReader reader;
	reader.setContentHandler(&handler);
	reader.setErrorHandler(&handler);
	bool res = reader.parse(source);
	if ( !res )
	{
		qWarning() << source->data();
		emit fileError(currentFileIndex, "Error parse server answer:" + handler.myErrorString);
	}
	serverAnswer->close();
	delete(source);
	source = NULL;
	return res;
// done. easy, huh?
}

void ImageShackClass::uploadFinish()
{
	delete myCurrentResultHash;
	myCurrentResultHash = 0;
// 
	delete serverAnswer;
	serverAnswer = 0;

	emit fileUploaded();
}

void ImageShackClass::fillStruct(UploadResult *Struct, QHash<QString, QString> *Hash)
{
	Struct->index = this->currentFileIndex;
	Struct->size = this->currentImageSize;
	Struct->ImageName = currentImageUploaded.ImageName;
	QHash<QString, QString> tmp = *Hash;
	Struct->CommonHtml = tmp["ad_link"];
	Struct->ImageHtml  = tmp["ad_link"];
	Struct->SessionHtml= tmp["ad_link"];
	Struct->Image      = tmp["image_link"];
	if ( tmp["thumb_exists"] == "yes" ) {
		Struct->Thumb      = tmp["thumb_link"];
	} else {
		qWarning() << "server did't create a thumb."
			<< "If your imagesize more than 200x200px tell ("+About.Author+") about this case";
		Struct->Thumb      = tmp["image_link"];
	}
	
//	Struct->success    = true;
}


void ImageShackClass::Canceled()
{
	qDebug() << "Canceled\n" << imageIterator->hasNext();
	httpRequestAborted = true;
	if ( http_h != 0 )
		http_h->abort();
}

void ImageShackClass::globalDestruction(QString errorMsg)
{
	qWarning() << "global Destruction on ERROR:" << errorMsg;
	if ( http_h != 0 ) {
		http_h->abort();
		httpRequestAborted = true;
	}
	delete serverAnswer;
	serverAnswer = 0;
	delete imageIterator;
	imageIterator = 0;
}

void ImageShackClass::resizeTogled(bool enable) {
	Config->setValue("optimage", enable);
}
void ImageShackClass::resizePolicySelected(int variant) {
	Config->setValue("optsize", variant);
}
void ImageShackClass::rembarTogled(bool enable) {
	Config->setValue("rembar", enable);
}









SaxHandler::SaxHandler(QHash<QString, QString> *myCurrentResult)
	: QXmlDefaultHandler()
{
	this->myCurrentResult = myCurrentResult;
	myCurrentResult->clear(); // QHash
	currentElement.clear(); // clear too
}

bool SaxHandler::startElement(const QString &, const QString &, 
				const QString &qName, 
				const QXmlAttributes &attribs)
{
	if ( attribs.count() > 0 ){
		qWarning() << qName << QObject::tr("element has %n attribute(s). Tell your maintainer about it."
				, "", attribs.count());
	}
	currentElement = qName;
	return true;
}

bool SaxHandler::characters(const QString &str)
{
	if ( !str.isNull() ) {
		if (! currentElement.isEmpty() ){
			myCurrentResult->insert(currentElement, str);
		}
	}
	return true;
}

bool SaxHandler::endElement(const QString &, const QString &,
				const QString &qName) 
{
	currentElement.clear();
	return true;
	qDebug() << "end of element" << qName;
}

bool SaxHandler::fatalError(const QXmlParseException &exception) {
	qWarning() << "Line" << exception.lineNumber() << ", column" << exception.columnNumber() <<
		":" << exception.message();
	this->myErrorString = "Line " + QString::number(exception.lineNumber()) 
		+ ", column " + QString::number(exception.columnNumber()) + ": " + exception.message();
	return false; 
}

QString ImageShackClass::randomString(int length)
{
	if (length <=0 ) return QString::null;

	QString str; //str.setLength( length );
	while (length--)
	{
                int r=rand() % 62;
		r+=48;
		if (r>57) r+=7;
		if (r>90) r+=6;
		str += char(r);
	// so what if I work backwards?
	}
	return str;
}


Q_EXPORT_PLUGIN2(ImageShack, ImageShackClass);
